<template>
  <div class="content" @click="isShowContextmenu = false">
    <el-row :gutter="20" style="height: 100%">
      <el-col :span="4" style="height: 100%">
        <el-card style="height: 100%">
          <el-form :model="form" label-width="80px">
            <el-form-item label="数据名称">
              <el-input v-model="form.name"></el-input>
            </el-form-item>
            <el-form-item label="数据描述">
              <el-input type="textarea" v-model="form.desc"></el-input>
            </el-form-item>
            <el-form-item label="数据权限">
              <el-radio-group v-model="form.openLevel">
                <el-radio label="1">私人</el-radio>
                <el-radio label="2">本角色</el-radio>
                <el-radio label="3">公开</el-radio>
              </el-radio-group>
            </el-form-item>
            <div style="text-align: center">
              <el-button type="primary" @click="onSubmit">提交</el-button>
              <el-button @click="back">返回</el-button>
            </div>
          </el-form>
        </el-card>
      </el-col>
      <el-col :span="20" style="height: 100%">
        <el-card style="height: 100%; position: relative" class="dnd-card">
          <div class="dnd-wrap" ref="dndContainerRef">
            <div class="title">基础组件</div>
            <div class="components">
              <div data-type="table" class="component" @mousedown="startDrag">
                <Table type="show" />
              </div>
              <div data-type="join" class="component" @mousedown="startDrag">
                <Join type="show" />
              </div>
              <div data-type="end" class="component" @mousedown="startDrag">
                <End type="show" />
              </div>
            </div>
          </div>
          <div id="container"></div>
          <!-- <div class="btns">
            <el-button @click="handleChangeMode">详细/简略模式</el-button>
          </div> -->
          <TeleportContainer />
        </el-card>
      </el-col>
    </el-row>
    <div class="contextmenu" v-show="isShowContextmenu" ref="contextmenuRef">
      <div class="menu-item" @click="handleEdgeDelete">删除</div>
    </div>
  </div>
</template>

<script setup>
import { dataHandleSave, dataHandleInfo } from '@/api/dataLink'
import { Graph, Shape } from '@antv/x6'
import { onMounted, reactive } from 'vue-demi'
import { getTeleport } from '@antv/x6-vue-shape'
import { Transform } from '@antv/x6-plugin-transform'
import { Snapline } from '@antv/x6-plugin-snapline'
import { Dnd } from '@antv/x6-plugin-dnd'
import End from './end.vue'
import Table from './table.vue'
import Join from './join.vue'
import './add'
import { tableNode, endNode, joinNode, endedNode } from './add'
import { getDatabaseList } from '@/api/datasourceCenter'
import { useAntvStore } from '@/store/modules/antvStore'
import { ElMessage } from 'element-plus'
const antvStore = useAntvStore()
const router = useRouter()
const route = useRoute()

const TeleportContainer = getTeleport()
const form = reactive({})
const graph = ref(null)
const dnd = ref(null)
const dndContainerRef = ref(null)
const contextmenuRef = ref(null)
const themeMode = ref(1)
const dataSourceList = ref([])
const isShowContextmenu = ref(false)
const currentEdge = ref('')
onMounted(() => {
  graph.value = new Graph({
    container: document.getElementById('container'),
    background: {
      color: '#F2F7FA'
    },
    panning: true,
    mousewheel: {
      enabled: true,
      modifiers: ['ctrl']
    },
    grid: {
      size: 10, // 网格大小 10px
      visible: true // 绘制网格，默认绘制 dot 类型网格
    },
    connecting: {
      router: {
        name: 'manhattan',
        args: {
          startDirections: ['top', 'right', 'bottom', 'left'],
          endDirections: ['top', 'right', 'bottom', 'left'],
          padding: {
            top: 20,
            right: 20,
            bottom: 20,
            left: 20
          }
        }
      },
      snap: {
        radius: 50 // 吸附半径
      },
      createEdge(a) {
        console.log(a)
        return new Shape.Edge({
          attrs: {
            line: {
              stroke: '#A2B1C3',
              strokeWidth: 2
            }
          }
          // tools: {
          //   name: 'button-remove',
          //   args: { distance: '50%' }
          // }
        })
      }
    }
  })
  // graph.value.on('edge:mouseenter', ({ cell }) => {
  //   cell.addTools([
  //     {
  //       name: 'source-arrowhead'
  //     },
  //     {
  //       name: 'target-arrowhead',
  //       args: {
  //         attrs: {
  //           fill: 'red'
  //         }
  //       }
  //     }
  //   ])
  // })

  // graph.value.on('edge:mouseleave', ({ cell }) => {
  //   cell.removeTools()
  // })
  graph.value.on('edge:contextmenu', ({ edge, e }) => {
    console.log(e)
    console.log(contextmenuRef.value.style)
    contextmenuRef.value.style.left = e.clientX + 'px'
    contextmenuRef.value.style.top = e.clientY + 'px'
    currentEdge.value = edge
    isShowContextmenu.value = true
  })
  graph.value.on('node:click', ({ node }) => {
    console.log(node)
  })
  graph.value.on('edge:added', ({ edge }) => {
    console.log(edge)
    const sourceNode = edge.getSourceNode()
  })
  graph.value.on('edge:mouseup', ({ edge }) => {
    console.log(edge)
    const sourceNode = edge.getSourceNode()
    const targetNode = edge.getTargetNode()
    // 未链接port的线，删除
    if (!edge.target.port) {
      graph.value.removeEdge(edge)
      return
    }
  })
  graph.value.on('edge:connected', ({ isNew, edge }) => {
    if (isNew) {
      // 对新创建的边进行插入数据库等持久化操作
      console.log(true, edge)
      const sourceNode = edge.getSourceNode()
      const targetNode = edge.getTargetNode()
      // table可连jointop  endtop
      if (sourceNode.shape == 'custom-vue-table') {
        if (
          (targetNode.shape == 'custom-vue-join' && edge.target.port == 'caseNodeTop') ||
          (targetNode.shape == 'custom-vue-end' && edge.target.port == 'caseNodeTop')
        ) {
          console.log('pass')
        } else {
          graph.value.removeEdge(edge)
          return
        }
      }
      // jointop逆向链接
      if (sourceNode.shape == 'custom-vue-join' && edge.source.port == 'caseNodeTop') {
        console.log('逆向')
        graph.value.removeEdge(edge)
        return
      }
      // joinbottom 可连 jointop endtop
      if (sourceNode.shape == 'custom-vue-join') {
        if (targetNode.id == sourceNode.id) {
          graph.value.removeEdge(edge)
          return
        }
        if (
          (targetNode.shape == 'custom-vue-join' && edge.target.port == 'caseNodeTop') ||
          (targetNode.shape == 'custom-vue-end' && edge.target.port == 'caseNodeTop')
        ) {
          console.log('pass')
        } else {
          graph.value.removeEdge(edge)
          return
        }
      }
      // end  不可链接
      if (sourceNode.shape == 'custom-vue-end' || sourceNode.shape == 'custom-vue-ended') {
        graph.value.removeEdge(edge)
        return
      }
      // 不可重复链接
      const edges = graph.value.getEdges()
      console.log(edges)
      let num = 0
      for (let i of edges) {
        if (
          i.source.cell == edge.source.cell &&
          i.target.cell == edge.target.cell &&
          i.source.port == edge.source.port &&
          i.target.port == edge.target.port
        ) {
          num++
        }
      }
      if (num > 1) {
        graph.value.removeEdge(edge)
        return
      }
      // 连入end节点的线，给end节点添加数据
      if (targetNode.shape == 'custom-vue-end') {
        console.log(sourceNode)
        if (sourceNode.shape == 'custom-vue-table') {
          targetNode.setData({
            fields: sourceNode.data.fieldList.map((item) => {
              return {
                ...item,
                joinReName: item.reName
              }
            })
          })
        } else {
          targetNode.setData({
            fields: sourceNode.data.choosedFields
          })
        }
      }
    } else {
      console.log(false, edge)
    }
  })
  graph.value.on('node:added', ({ node }) => {
    console.log(node)
    console.log(node.position())
    if (node.shape == 'custom-vue-end') {
      const nNode = graph.value.createNode({
        ...endedNode,
        position: { x: node.position().x + 150, y: node.position().y + 300 }
      })
      graph.value.addNode(nNode)
      graph.value.addEdge({
        source: { cell: node.id, port: 'caseNodeBottom' }, // 源节点和链接桩 ID
        target: { cell: nNode.id, port: 'caseNodeTop' }, // 目标节点 ID 和链接桩 ID
        attrs: {
          line: {
            stroke: '#A2B1C3',
            strokeWidth: 2
          }
        }
      })
    }
  })
  graph.value.use(
    new Transform({
      resizing: {
        enabled: true,
        minWidth: 100,
        maxWidth: 600,
        minHeight: 50,
        maxHeight: 300,
        orthogonal: false,
        restrict: false,
        preserveAspectRatio: false
      }
    })
  )
  graph.value.use(
    new Snapline({
      enabled: true
    })
  )
  dnd.value = new Dnd({
    target: graph.value,
    scaled: false,
    dndContainer: dndContainerRef.value,
    getDropNode(e) {
      console.log(e)
      const node = e.clone()
      if (e.shape == 'custom-vue-table') {
        node.setData({
          dataSourceList: dataSourceList.value
        })
      }
      return node
    }
  })
  console.log(antvStore)
  antvStore.setGraph(graph.value)
  getDatabaseList().then((res) => {
    dataSourceList.value = res.data
  })
  console.log(route.query.id)
  if (route.query.id) {
    dataHandleInfo(route.query.id).then((res) => {
      form.name = res.data.name
      form.desc = res.data.desc
      form.openLevel = res.data.openLevel
      const cells = []
      res.data.graphJson.cells.forEach((item) => {
        if (item.shape === 'edge') {
          cells.push(graph.value.createEdge(item))
        } else {
          cells.push(graph.value.createNode(item))
        }
      })
      graph.value.resetCells(cells)
    })
  }
})
const startDrag = (e) => {
  const target = e.currentTarget
  const type = target.getAttribute('data-type')
  let node = ''
  if (type === 'end') {
    node = graph.value.createNode(endNode)
  } else if (type === 'table') {
    node = graph.value.createNode(tableNode)
  } else if (type === 'join') {
    node = graph.value.createNode(joinNode)
  }
  // 传e就可以了，e.nativeEvent为react示例
  dnd.value.start(node, e)
}
const handleChangeMode = () => {
  if (themeMode.value == 0) {
    console.log(graph.value.toJSON())
    themeMode.value = 1
  } else {
    console.log(graph.value.toJSON())
    themeMode.value = 0
  }
}
const back = () => {
  router.back()
}
const onSubmit = () => {
  console.log(graph.value.toJSON())
  const params = {
    handle: {
      table: '',
      alias: '',
      type: '',
      fields: [],
      from: [],
      where: {}
    },
    name: form.name,
    desc: form.desc,
    openLevel: form.openLevel,
    graphJson: graph.value.toJSON(),
    tableList: [],
    metadata: []
  }
  const end = graph.value.getNodes().find((item) => item.shape == 'custom-vue-end')
  const endEdge = graph.value.getEdges().find((item) => {
    return item.target.cell == end.id
  })
  const endNode = graph.value.getNodes().find((item) => item.shape == 'custom-vue-end')
  if (end && endEdge) {
    params.handle = setJson(endEdge.getSourceNode())
    for (let i = 0; i < params.handle.fields.length; i++) {
      params.handle.fields[i] = {
        ...params.handle.fields[i],
        ...getFieldConvert(params.handle.fields[i], endNode)
      }
    }
  } else {
    return
  }
  const tableNodes = graph.value.getNodes().filter((item) => item.shape == 'custom-vue-table')
  if (tableNodes.length) {
    params.tableList = setJsonTable(tableNodes)
  } else {
    return
  }
  params.metadata = setJsonMetadata(endNode)
  if (route.query.id) {
    params._id = route.query.id
  }
  console.log(params)
  dataHandleSave(params).then((res) => {
    ElMessage.success('保存成功')
  })
}
const setJson = (node) => {
  const edges = graph.value.getEdges().filter((item) => {
    return item.getTargetNode().id == node.id
  })
  console.log(edges)
  const fromList = []
  edges.length &&
    edges.forEach((item) => {
      const sourceNode = item.getSourceNode()
      fromList.push(setJson(sourceNode))
    })
  if (node.shape == 'custom-vue-table') {
    console.log(node)
    return {
      table: node.data.tableId,
      alias: node.data.tableReName,
      type: 'table',
      fields: node.data.fieldList.map((item) => {
        return {
          name: item.COLUMN_NAME,
          alias: item.reName
        }
      }),
      dataSourceId: node.data.datasourceId,
      join: getJoinType(node.data?.sonJoinWhere?.type),
      on: getOnSql(node.data?.sonJoinWhere?.on)
    }
  } else {
    return {
      table: node.data.joinName,
      alias: node.data.joinName,
      type: 'join',
      fields: node.data.choosedFields.map((item) => {
        return {
          name: `${item.tableName}.${item.reName}`,
          alias: item.joinReName
        }
      }),
      from: fromList,
      where: getJoinCondition(node.data.condition, node.data.choosedFields)
    }
  }
}
const setJsonTable = (nodes) => {
  console.log(nodes)
  return nodes.map((item) => {
    return {
      dataSourceId: item.data.datasourceId,
      tableName: item.data.tableId,
      fields: item.data.fieldList.map((i) => {
        return {
          name: i.COLUMN_NAME,
          alias: i.reName
        }
      }),
      where: getTableCondition(item.data.condition, item.data.datasourceId)
    }
  })
}
const setJsonMetadata = (node) => {
  console.log(node.data)
  const list = []
  if (node && node.data.finalFields) {
    node.data.finalFields.forEach((item) => {
      const params = {
        id: item.joinReName,
        column: item.joinReName,
        comment: item.FIELD_NAME || item.COMMENTS
      }
      if (item.tag || item.customTag) {
        params.type = 1
        params.dict = item.dict
      } else {
        params.type = 0
      }
      list.push(params)
    })
  }
  return list
}
const getJoinType = (type) => {
  switch (type) {
    case 'left':
      return 'left join'
    case 'right':
      return 'right join'
    case 'inner':
      return 'inner join'
    default:
      return ''
  }
}
const getOnSql = (list) => {
  let sql = ``
  if (list && list.length) {
    list.forEach((item, index) => {
      if (index == 0) {
        sql += `${item.source}.${item.sourceTable} = ${item.target}.${item.targetTable}`
      } else {
        sql += ` and ${item.source}.${item.sourceTable} = ${item.target}.${item.targetTable}`
      }
    })
  }
  return sql
}
const getJoinCondition = (condition, fields) => {
  console.log(condition, fields)
  const params = getJson(condition.children, fields)
  return params.length > 0 ? params[0] : {}
  function getJson(list, field) {
    if (list.length > 0) {
      const data = []
      list.forEach((item) => {
        if (item.type == 'condition') {
          data.push({
            type: 'connect',
            connect: item.condition,
            children: getJson(item.children, field)
          })
        } else if (item.type == 'field') {
          data.push({
            type: 'condition',
            field: item.value,
            dataSourceType: getSourceType(item.value, fields),
            operator: getOperator(item.compare),
            valueType: item.compareType,
            value: item.compareValue
          })
        }
      })
      return data
    }
    return {}
  }
}
const getFieldSql = (value, fields) => {
  const field = fields.find((item) => item.joinReName == value)
  if (field) {
    return `${field.tableName}.${field.reName}`
  }
  return ''
}
const getSourceType = (value, fields) => {
  const field = fields.find((item) => item.tableName + '.' + item.COLUMN_NAME == value)
  if (field) {
    const datasource = dataSourceList.value.find((item) => item._id == field.datasourceId)
    if (datasource) {
      return datasource.type
    }
    return ''
  }
  return ''
}
const getTableCondition = (condition, datasourceId) => {
  console.log(condition)
  const params = getJson(condition.children, datasourceId)
  return params.length > 0 ? params[0] : {}
  function getJson(list, datasourceId) {
    if (list.length > 0) {
      const data = []
      list.forEach((item) => {
        if (item.type == 'condition') {
          data.push({
            type: 'connect',
            connect: item.condition,
            children: getJson(item.children, datasourceId)
          })
        } else if (item.type == 'field') {
          data.push({
            type: 'condition',
            field: item.value,
            dataSourceType: getTableSourceType(datasourceId),
            operator: getOperator(item.compare),
            valueType: item.compareType,
            value: item.compareValue
          })
        }
      })
      return data
    }
    return {}
  }
}
const getTableSourceType = (value) => {
  const datasource = dataSourceList.value.find((item) => item._id == value)
  if (datasource) {
    return datasource.type
  }
  return ''
}
const getFieldConvert = (field, endNode) => {
  console.log(field, endNode)
  if (endNode && endNode.data.finalFields) {
    const finalField = endNode.data.finalFields.find(
      (item) => field.name == `${item.tableName}.${item.reName}` || field.name == `${item.reName}`
    )
    if (finalField && finalField.change) {
      console.log(finalField)
      return {
        convert: {
          case: finalField.changeList.map((i) => {
            return {
              value: i.changeValue,
              condition: getTableCondition(i.condition, '')
            }
          }),
          else: {
            type: 'value',
            value: finalField.defaultValue
          }
        }
      }
    }
  }
  return {}
}
const getOperator = (value) => {
  switch (value) {
    case '=': {
      return 'eq'
    }
    case '!=': {
      return 'ne'
    }
    case '>': {
      return 'gt'
    }
    case '<': {
      return 'lt'
    }
    case '>=': {
      return 'gte'
    }
    case '<=': {
      return 'lte'
    }
    case 'like': {
      return 'like'
    }
    case 'not like': {
      return 'notLike'
    }
    case 'in': {
      return 'in'
    }
    case 'not in': {
      return 'notIn'
    }
    case 'isNull': {
      return 'isNull'
    }
    case 'notNull': {
      return 'notNull'
    }
    case 'startsWith': {
      return 'startsWith'
    }
    case 'endsWith': {
      return 'endsWith'
    }
    default:
      return ''
  }
}
const handleEdgeDelete = () => {
  graph.value.removeEdge(currentEdge.value)
}
// import res from './text.json'
// onMounted(() => {
//   console.log(res)
//   setTimeout(() => {
//     const cells = []
//     res.cells.forEach((item) => {
//       if (item.shape === 'edge') {
//         cells.push(graph.value.createEdge(item))
//       } else {
//         cells.push(graph.value.createNode(item))
//       }
//     })
//     graph.value.resetCells(cells)
//     // onSubmit()
//   }, 500)
// })
</script>

<style lang="scss" scoped>
#container {
  position: relative;
  left: 150px;
  width: calc(100% - 150px);
  height: 100%;
}
.dnd-wrap {
  position: absolute;
  z-index: 9;
  left: 0;
  top: 0;
  height: 100%;
  background-color: #fff;
  width: 150px;
  box-shadow: 0 0 10px #ccc;
  border-radius: 10px;
  padding: 10px;
  .title {
    padding: 10px 0 20px;
    text-align: center;
    font-weight: 700;
  }
  .components {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    .component {
      width: 100%;
      height: 50px;
      margin-bottom: 10px;
      cursor: move;
    }
  }
}
.btns {
  position: absolute;
  right: 20px;
  top: 20px;
  z-index: 9;
}
.dnd-card {
  :deep(.el-card__body) {
    padding: 0;
  }
}
.contextmenu {
  position: absolute;
  left: 0;
  top: 0;
  min-width: 150px;
  padding: 10px 0;
  background-color: #fff;
  box-shadow: 0 0 10px #ccc;
  border-radius: 5px;
  .menu-item {
    cursor: pointer;
    line-height: 30px;
    padding: 5px 20px;
    color: #3b3b3b;
    &:hover {
      background-color: rgba(0, 0, 0, 0.05);
    }
  }
}
:deep(.el-card__body) {
  height: 100%;
}
</style>
